<!DOCTYPE html>
<html>
  <head>
    <meta charset='utf-8'>

    <link rel="stylesheet" type="text/css" 
      href="/assets/css/straybirds.css" media="screen" />
    <link rel="stylesheet" type="text/css" 
      href="/assets/css/pygments.css" media="screen" />

    <!-- MathJax Section Start -->

    <script type="text/javascript"
    src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
    </script>
    <script>
        MathJax.Hub.Config({
              tex2jax: {
              skipTags: ['script', 'noscript', 'style', 'textarea', 'pre']
              }
        });
        MathJax.Hub.Queue(function() {
            var all = MathJax.Hub.getAllJax(), i;
            for(i=0; i < all.length; i += 1) {
                all[i].SourceElement().parentNode.className += ' has-jax';
            }
        });
    </script>

    <!-- MathJax Section End -->

    <!-- Google Analytics Start-->
    <script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-48100787-1', 'minixalpha.github.io');
  ga('send', 'pageview');

</script>

    <!-- Google Analytics End -->

    <title>单例模式</title>
  </head>

  <body>
    <div class="container">
      <header>
        <div class="container">
          <h1>
              <a href="/" title="Home Page"> 潇湘夜雨 </a>
          <span class="github-src">
            <a href ="https://github.com/minixalpha/minixalpha.github.io"
               target="_blank"
               title="Fork me on GitHub">
              <img src="/assets/images/GitHub-Mark-Light-32px.png" alt="">
            </a>
          </span>
          </h1>
        </div>
      </header>

      <aside id="left-side">
        <h2> 分类 </h2>
  <ul class="category-list">
      
            
                <li>
                <a href="/categories/源代码阅读"> 源代码阅读 (20) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/设计模式"> 设计模式 (3) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/科研"> 科研 (6) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/思想"> 思想 (2) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/技术"> 技术 (18) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/工具"> 工具 (4) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/虚拟机"> 虚拟机 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/java"> java (11) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/c语言"> c语言 (4) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/读书"> 读书 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/操作系统"> 操作系统 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/英语"> 英语 (10) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/计算机系统"> 计算机系统 (3) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/生活"> 生活 (1) </a>
                </li>
            
      
  </ul>

      </aside>

      <aside id="right-side">
        <h2> 归档 </h2>
  <ul class="archive-list">
    
    
    
        
        
        
        
            
            <li>
                <a href="/2014/08">
                    2014-08 (1)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/07">
                    2014-07 (5)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/05">
                    2014-05 (12)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/04">
                    2014-04 (3)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/03">
                    2014-03 (11)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/02">
                    2014-02 (6)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/01">
                    2014-01 (3)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2013/11">
                    2013-11 (10)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2013/10">
                    2013-10 (3)
                </a>
            </li>

        
        
    
        
        
        
        
            
            <li>
                <a href="/2010/09">
                    2010-09 (1)
                </a>
            </li>

        
        
    
  </ul>

      </aside>

      <article>

<h1>单例模式</h1>

<hr>

<ul>
<li>定义 </li>
</ul>

<p>单例模式确保一个类只有一个实例，并提供一个全局访问点</p>

<ul>
<li>解释</li>
</ul>

<p>从定义可以看出，特点是这个类只有一个实例。那么，为什么要这么做呢？原因在于，有些时候，这个类只有一个实例会节约资源，或者只有一个实例才能保证整个程序运行正确，一致。例如：线程池，缓存，对话框，日志对象等等 。</p>

<ul>
<li>示例</li>
</ul>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">class</span> <span class="nc">Singleton</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="n">Singleton</span> <span class="n">singleton</span><span class="o">;</span>

    <span class="kd">private</span> <span class="nf">Singleton</span><span class="o">()</span> <span class="o">{</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="n">Singleton</span> <span class="nf">getInstance</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">singleton</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">singleton</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Singleton</span><span class="o">();</span>
        <span class="o">}</span>
        <span class="k">return</span> <span class="n">singleton</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div>
<p>这是单例的经典使用方式：</p>

<ul>
<li>一个 private static 对象</li>
<li>构造器设置为 private</li>
<li>一个 public static 方法提供全局访问点 </li>
</ul>

<p>开始的时候，其实我比较困惑为什么不在 singleton 声明处直接实例化对象，后来明白了，这是一种延迟实例化的手段，保证只在需要时才实例化。如果直接在声明时实例化，那么只要类加载了，即使不需要对象，也会对它进行实例化。</p>

<p>另外，这个经典使用方式其实是有问题的，对比后面 Tomcat 中的应用场景，你可能会发现问题所在。</p>

<p>在 Tomcat 中，就有一个单例模式，它是 <code>org.apache.catalina.tribes.util.StringManager</code> 类。在 Tomcat 中，会有许多地方需要对错误消息进行处理。我们使用 <code>StringManager</code> 类来管理这些错误消息。</p>

<p>错误消息首先不能硬编码到代码中，否则需要提供国际化支持时，就会很痛苦。错误消息需要定义在配置文件中。Tomcat 为每个包 (package) 都提供了三种语言的错误消息配置文件。每个包内都有很多类，我们没必要为每个类都生成一个 <code>StringManager</code> 类，因为它们共享同一个配置文件。所以，一个包只需要一个 <code>StringManager</code> 对象就好了。我们怎么为一个包生成一个 <code>StringManager</code> 呢？ Tomcat 在 <code>StringManager</code> 内部保存着所有包的 <code>StringManager</code> 实例，你需要一个实例时，只需要提供包名，调用 <code>StringManager</code> 的相应方法，就会返回与此包名对应的 <code>StringManager</code> 实例。下面是相关的代码，一目了然。</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">StringManager</span> <span class="o">{</span> 

<span class="kd">private</span> <span class="nf">StringManager</span><span class="o">(</span><span class="n">String</span> <span class="n">packageName</span><span class="o">)</span> <span class="o">{</span>
    <span class="o">...</span>
<span class="o">}</span>

<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">Hashtable</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">StringManager</span><span class="o">&gt;</span> <span class="n">managers</span> <span class="o">=</span>
<span class="k">new</span> <span class="n">Hashtable</span><span class="o">&lt;&gt;();</span>

<span class="cm">/**</span>
<span class="cm">* Get the StringManager for a particular package. If a manager for</span>
<span class="cm">* a package already exists, it will be reused, else a new</span>
<span class="cm">* StringManager will be created and returned.</span>
<span class="cm">*</span>
<span class="cm">* @param packageName The package name</span>
<span class="cm">*/</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kd">synchronized</span> <span class="n">StringManager</span> <span class="nf">getManager</span><span class="o">(</span><span class="n">String</span> <span class="n">packageName</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">StringManager</span> <span class="n">mgr</span> <span class="o">=</span> <span class="n">managers</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">packageName</span><span class="o">);</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">mgr</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">mgr</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringManager</span><span class="o">(</span><span class="n">packageName</span><span class="o">);</span>
        <span class="n">managers</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">packageName</span><span class="o">,</span> <span class="n">mgr</span><span class="o">);</span>
    <span class="o">}</span>
    <span class="k">return</span> <span class="n">mgr</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div>
<p>使用了一个 <code>Hashtable</code> 来保存 <code>managers</code>，每次通过 <code>getManager</code> 方法，通过包名访问，如果访问不到，就为此包新生成一个 <code>StringManager</code> 实例。</p>

<p>有没有注意到 getManager 方法被  <code>synchronized</code> 修饰？这就是之前我们举的经典示例时说的问题。在使用单例时，只有一个对象，这个对象可能是被多个线程共享的。如果不同步，就可能会出现数据不一致的情况。例如，两个线程同时调用了 <code>getManager</code>，访问同一个包名。正确的执行是，其中一个线程第一次调用时，mgr 为空，此时生成一个 StringManager。第二个线程调用时，mgr 就不为空了。但是，如果不同步，那么当第一个线程通过了 <code>if(mgr == null)</code> 时，此时线程被切换了，这时，第二个线程也会通过 <code>if(mgr == null)</code> ，这样就导致同一个包，生成了两个 <code>StringManager</code>。</p>

<ul>
<li>扩展阅读 </li>
</ul>

<p>关于单例模式与线程安全，建议阅读一下这篇文章：</p>

<p><a href="http://blog.csdn.net/haoel/article/details/4028232">深入浅出单实例Singleton设计模式</a></p>

<p>关于单例模式的其它实现方式，可以阅读 </p>

<p><a href="http://cantellow.iteye.com/blog/838473">单例模式的七种写法</a></p>


      </article>

      <div class="comments">
        
          <div id="disqus_thread"></div>
 <script type="text/javascript">
     /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
     var disqus_shortname = 'minixalpha'; // required: replace example with your forum shortname

     /* * * DON'T EDIT BELOW THIS LINE * * */
     (function() {
         var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
         dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
         (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
     })();
 </script>
 <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
    <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
    

        
      </div>


      <footer>
        Copyright (c) minixalpha 2014
      </footer>

    </div>
  </body>
</html>
